﻿using Nutrapp.DBAL;
using System;
using System.Collections.Generic;
using System.Data.Objects.SqlClient;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Nutrapp.Controllers
{
    public class SearchController : Controller
    {
        //
        // GET: /Search/



        public ActionResult Index()
        {
            List<string> ingredients = new List<string>();
            List<string> recipeNames = new List<string>();
            List<string> name_and_ingredient = new List<string>();
            using (NutrappDataContext context = new NutrappDataContext())
            {
                ingredients = (from i in context.Ingredients
                               select i.Name).ToList();
                recipeNames = (from name in context.RecipeNameKeyWords
                               select name.KeyWord).ToList();


                name_and_ingredient = ingredients.Union(recipeNames).ToList();

            }
            return View(name_and_ingredient);
        }

        public ActionResult Advanced()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Result(String[] search, string search_rec)
        {
            List<Recipe> StrictSearchResult = new List<Recipe>();
            List<Recipe> LooseSearchResult = new List<Recipe>();
            List<string> searchList = new List<string>();

            try
            {
                searchList = search.ToList();
            }
            catch (ArgumentNullException)
            {
                searchList.Clear();
            }

            List<string> name_keywords = new List<string>();
            List<string> ingrdient_keywords = new List<string>();
            using (NutrappDataContext context = new NutrappDataContext())
            {
                name_keywords = (from n in context.RecipeNameKeyWords
                                 where searchList.Contains(n.KeyWord.ToLower())
                                 select n.KeyWord).ToList();
                ingrdient_keywords = (from n in context.Ingredients
                                      where searchList.Contains(n.Name.ToLower())
                                      select n.Name).ToList();

            }

            //search by name
            List<Recipe> searchStrictName = new List<Recipe>();
            List<Recipe> searchlooseName = new List<Recipe>();
            if (name_keywords.Count != 0)
            {
                searchStrictName = strict_search_for_recipename(name_keywords);
                searchlooseName = loose_search_for_recipename(name_keywords);
                searchlooseName.RemoveAll(r => searchStrictName.Exists(c => c.ID == r.ID));
            }

            //search by ingredient
            List<Recipe> searchStrictIngrdient = new List<Recipe>();
            List<Recipe> searchLooseIngredient = new List<Recipe>();
            if (ingrdient_keywords.Count != 0)
            {
                searchStrictIngrdient = strict_seach_for_ingredeints(ingrdient_keywords);
                searchLooseIngredient = loose_search_for_ingredeints(ingrdient_keywords);
                searchLooseIngredient.RemoveAll(a => searchStrictIngrdient.Exists(b => a.ID == b.ID));
            }



            //Generate strict and loose search result

            //Intersect lists
            if (searchStrictIngrdient.Count != 0 && searchStrictName.Count != 0)
            {
                StrictSearchResult = (from r1 in searchStrictName from r2 in searchStrictIngrdient where r1.ID == r2.ID select r1).ToList();
            }
            else if (searchStrictIngrdient.Count == 0)
            {
                StrictSearchResult = searchStrictName;
            }
            else if (searchStrictName.Count == 0)
            {
                StrictSearchResult = searchStrictIngrdient;
            }

            // Nutrition search
            List<Recipe> searchStrictNutrition = new List<Recipe>();
            List<Recipe> searchLooseNutrition = new List<Recipe>();

            Dictionary<string, String[]> searchDict = new Dictionary<string, String[]>();
            List<string> namesIngrList = new List<string>();


            try
            {

                foreach (String s in searchList)
                {
                    if (name_keywords.Any(str => str.Contains(s)) && s != "")
                    {
                        namesIngrList.Add(s);
                    }

                    if (ingrdient_keywords.Any(str => str.Contains(s)) && s != "")
                    {
                        namesIngrList.Add(s);
                    }
                }

                foreach (String s in namesIngrList)
                {
                    searchList.RemoveAll(x => x == s);
                }

                for (int c = 0; c < searchList.Count; c++)
                {



                    if (c == 0 && searchList[c] != "")
                    {
                        if (searchList[c + 1] != "")
                        {

                            int fromInt = int.Parse(searchList[c]);
                            int toInt = int.Parse(searchList[c + 1]);

                            if (fromInt > 0 && fromInt < 3000 && toInt > fromInt && toInt <= 3000)
                            {
                                string from = searchList[c];
                                string to = searchList[c + 1];
                                searchDict.Add("calories", new string[2] { from, to });
                            }
                            else
                            {
                                Session["valueError"] = "Please insert correct range of calories.";
                                return RedirectToAction("Index", "Search");
                            }
                        }
                        else
                        {
                            Session["valueError"] = "Please insert correct range of calories. Both fields must be filled.";
                            return RedirectToAction("Index", "Search");
                        }
                    }



                    if (c == 2 && searchList[c] != "")
                    {
                        if (searchList[c + 1] != "")
                        {
                            int fromInt = int.Parse(searchList[c]);
                            int toInt = int.Parse(searchList[c + 1]);

                            if (fromInt > 0 && fromInt < 900 && toInt > fromInt && toInt <= 900)
                            {
                                string from = searchList[c];
                                string to = searchList[c + 1];
                                searchDict.Add("fat", new string[2] { from, to });
                            }
                            else
                            {
                                Session["valueError"] = "Please insert correct range of fats.";
                                return RedirectToAction("Index", "Search");
                            }
                        }
                        else
                        {
                            Session["valueError"] = "Please insert correct range of fats. Both fields must be filled.";
                            return RedirectToAction("Index", "Search");
                        }
                    }


                    if (c == 4 && searchList[c] != "")
                    {
                        if (searchList[c + 1] != "")
                        {
                            int fromInt = int.Parse(searchList[c]);
                            int toInt = int.Parse(searchList[c + 1]);

                            if (fromInt > 0 && fromInt < 900 && toInt > fromInt && toInt <= 900)
                            {
                                string from = searchList[c];
                                string to = searchList[c + 1];
                                searchDict.Add("carbohydrate", new string[2] { from, to });
                            }
                            else
                            {
                                Session["valueError"] = "Please insert correct range of carbohydrates.";
                                return RedirectToAction("Index", "Search");
                            }
                        }
                        else
                        {
                            Session["valueError"] = "Please insert correct range of carbohydrates. Both fields must be filled.";
                            return RedirectToAction("Index", "Search");
                        }
                    }



                    if (c == 6 && searchList[c] != "")
                    {
                        if (searchList[c + 1] != "")
                        {
                            int fromInt = int.Parse(searchList[c]);
                            int toInt = int.Parse(searchList[c + 1]);

                            if (fromInt > 0 && fromInt < 900 && toInt > fromInt && toInt <= 900)
                            {
                                string from = searchList[c];
                                string to = searchList[c + 1];
                                searchDict.Add("protein", new string[2] { from, to });
                            }
                            else
                            {
                                Session["valueError"] = "Please insert correct range of proteins.";
                                return RedirectToAction("Index", "Search");
                            }
                        }
                        else
                        {
                            Session["valueError"] = "Please insert correct range of proteins. Both fields must be filled.";
                            return RedirectToAction("Index", "Search");
                        }
                    }
                }
            }

            catch (ArgumentNullException)
            {
                searchDict.Clear();
            }

            List<Recipe> searchStrict = new List<Recipe>();
            List<Recipe> searchLoose = new List<Recipe>();

            searchStrictNutrition = strict_seach_for_nutritions(searchDict);
            searchLooseNutrition = loose_seach_for_nutritions(searchDict);

            if (StrictSearchResult.Count != 0 && searchStrictNutrition.Count != 0)
            {
                StrictSearchResult = (from r1 in StrictSearchResult from r2 in searchStrictNutrition where r1.ID == r2.ID select r1).ToList();
            }
            else if (StrictSearchResult.Count == 0)
            {
                StrictSearchResult = searchStrictNutrition;
            }

            //Union four lists
            LooseSearchResult = (searchStrictName.Union(searchStrictIngrdient).Union(searchLooseNutrition).GroupBy(x => x.ID).Select(c => c.First()))
                                .Union(searchlooseName.Union(searchLooseIngredient).Union(searchLooseNutrition).GroupBy(x => x.ID).Select(c => c.First()))
                                .GroupBy(x => x.ID).Select(c => c.First()).ToList();

            var tuple = new Tuple<List<Recipe>, List<Recipe>>(StrictSearchResult, LooseSearchResult);
            return View(tuple);

        }

        public List<Recipe> strict_search_for_recipename(List<string> keywords)
        {
            using (NutrappDataContext context = new NutrappDataContext())
            {
                //strict search recipes based on name key words
                var list = from s in context.Recipes
                           where
                                (from i in context.RecipeNameKeyWords
                                 where keywords.Contains(i.KeyWord)
                                 select i.ID).Except(
                                from r in context.Recipe_KeyWords
                                where r.RID == s.ID
                                select r.KID
                                ).Count() == 0
                           select s;
                List<Recipe> rs = list.ToList();
                return rs;
            }
        }

        public List<Recipe> loose_search_for_recipename(List<string> keywords)
        {
            using (NutrappDataContext context = new NutrappDataContext())
            {
                //strict search recipes based on name key words
                var list = from s in context.Recipes
                           where
                                (from i in context.RecipeNameKeyWords
                                 where keywords.Contains(i.KeyWord)
                                 select i.ID).Intersect(
                                from r in context.Recipe_KeyWords
                                where r.RID == s.ID
                                select r.KID
                                ).Count() != 0
                           select s;
                List<Recipe> rs = list.ToList();
                return rs;
            }
        }

        public List<Recipe> strict_seach_for_ingredeints(List<string> ingredientList)
        {
            using (NutrappDataContext context = new NutrappDataContext())
            {
                List<int> ingredient_list = new List<int>();
                for (int i = 0; i < ingredientList.Count; i++)
                {
                    Ingredient ingredient = context.Ingredients.FirstOrDefault(j => j.Name.Equals(ingredientList[i]));
                    ingredient_list.Add(ingredient.ID);
                }

                var list = from s in context.Recipes
                           where
                                (from i in context.Ingredients
                                 where ingredient_list.Contains(i.ID)
                                 select i.ID).Except(
                                from r in context.Recipe_Ingredients
                                where r.RID == s.ID
                                select r.IID
                                ).Count() == 0
                           select s;
                List<Recipe> rs = list.ToList();
                return rs;
            }



        }

        public List<Recipe> loose_search_for_ingredeints(List<string> ingredientList)
        {
            using (NutrappDataContext context = new NutrappDataContext())
            {
                var list = from s in context.Recipes
                           where
                                (from i in context.Ingredients
                                 where ingredientList.Contains(i.Name)
                                 select i.ID).Intersect(
                                from r in context.Recipe_Ingredients
                                where r.RID == s.ID
                                select r.IID
                                ).Count() != 0
                           select s;
                List<Recipe> rs = list.ToList();
                return rs;
            }
        }

        // for testing purpose
        public List<Recipe> strict_search_by_Ingredients(List<string> ingredientList)
        {

            using (NutrappDataContext context = new NutrappDataContext())
            {
                List<int> ingredient_list = new List<int>();
                for (int i = 0; i < ingredientList.Count; i++)
                {
                    Ingredient ingredient = context.Ingredients.FirstOrDefault(j => j.Name.Equals(ingredientList[i]));
                    ingredient_list.Add(ingredient.ID);
                }


                var recipeIDs = new List<int>();
                for (int i = 0; i < ingredient_list.Count; i++)
                {
                    var temperary = (from x in context.Recipe_Ingredients
                                     where x.IID == ingredient_list[i]
                                     select x.RID).Distinct();
                    if (recipeIDs.Count == 0)
                    {
                        recipeIDs = temperary.ToList();
                    }
                    else
                    {
                        List<int> newList = new List<int>();
                        foreach (int item in recipeIDs.Intersect(temperary.ToList()))
                            newList.Add(item);
                        recipeIDs = newList;
                    }
                }


                var result = context.Recipes.Where(x => recipeIDs.Contains(x.ID)).ToList();
                List<Recipe> recipes = result;
                return recipes;
            }
        }

        //for testing purpose
        public List<Recipe> loose_search_by_Ingredients(List<string> ingredientList)
        {

            using (NutrappDataContext context = new NutrappDataContext())
            {
                List<int> ingredient_list = new List<int>();
                for (int i = 0; i < ingredientList.Count; i++)
                {
                    Ingredient ingredient = context.Ingredients.FirstOrDefault(j => j.Name.Equals(ingredientList[i]));
                    ingredient_list.Add(ingredient.ID);
                }


                var recipeIDs = new List<int>();
                for (int i = 0; i < ingredient_list.Count; i++)
                {
                    var temperary = (from x in context.Recipe_Ingredients
                                     where x.IID == ingredient_list[i]
                                     select x.RID).Distinct();
                    if (recipeIDs.Count == 0)
                    {
                        recipeIDs = temperary.ToList();
                    }
                    else
                    {
                        foreach (var item in temperary.ToList())
                        {
                            if (!recipeIDs.Contains(item))
                            {
                                recipeIDs.Add(item);
                            }
                        }

                    }
                }


                var result = context.Recipes.Where(x => recipeIDs.Contains(x.ID)).ToList();
                List<Recipe> recipes = result;
                return recipes;
            }
        }



        public List<Recipe> loose_seach_for_nutritions(Dictionary<string, String[]> searchDict)
        {
            using (NutrappDataContext context = new NutrappDataContext())
            {
                List<Recipe> calList = new List<Recipe>();
                List<Recipe> fatList = new List<Recipe>();
                List<Recipe> carbList = new List<Recipe>();
                List<Recipe> protList = new List<Recipe>();

                List<List<Recipe>> allRes = new List<List<Recipe>>();

                foreach (KeyValuePair<string, String[]> item in searchDict)
                {
                    if (item.Key == "calories")
                    {
                        int calValueFrom = (int.Parse(item.Value[0]) + int.Parse(item.Value[1])) / 2;
                        calList = context.Recipes.OrderBy(n => Math.Abs(n.Calories.Value - calValueFrom)).Take(10).ToList();
                        allRes.Add(calList);

                    }
                    if (item.Key == "fat")
                    {
                        int fatValueFrom = (int.Parse(item.Value[0]) + int.Parse(item.Value[1])) / 2;
                        fatList = context.Recipes.OrderBy(n => Math.Abs(n.Fat.Value - fatValueFrom)).Take(10).ToList();
                        allRes.Add(fatList);
                    }
                    if (item.Key == "carbohydrate")
                    {
                        int carbValueFrom = (int.Parse(item.Value[0]) + int.Parse(item.Value[1])) / 2;
                        carbList = context.Recipes.OrderBy(n => Math.Abs(n.Carbohydrate.Value - carbValueFrom)).Take(10).ToList();
                        allRes.Add(carbList);
                    }
                    if (item.Key == "protein")
                    {
                        int protValueFrom = (int.Parse(item.Value[0]) + int.Parse(item.Value[1])) / 2;
                        carbList = context.Recipes.OrderBy(n => Math.Abs(n.Protein.Value - protValueFrom)).Take(10).ToList();
                        allRes.Add(protList);
                    }
                }


                List<Recipe> finalList = new List<Recipe>();

                for (int i = 0; i < allRes.Count; i++)
                {
                    if (i == 0)
                    {
                        finalList = allRes[0];
                    }
                    else
                    {
                        finalList = finalList.Intersect(allRes[i]).ToList();
                    }
                }
                return finalList;
            }
        }

        public List<Recipe> strict_seach_for_nutritions(Dictionary<string, String[]> searchDict)
        {
            using (NutrappDataContext context = new NutrappDataContext())
            {
                List<Recipe> calList = new List<Recipe>();
                List<Recipe> fatList = new List<Recipe>();
                List<Recipe> carbList = new List<Recipe>();
                List<Recipe> protList = new List<Recipe>();

                List<List<Recipe>> allRes = new List<List<Recipe>>();

                foreach (KeyValuePair<string, String[]> item in searchDict)
                {
                    if (item.Key == "calories")
                    {

                        int calValueFrom = int.Parse(item.Value[0]);
                        int calValueTo = int.Parse(item.Value[1]);
                        calList = context.Recipes.Where(n => n.Calories >= calValueFrom && n.Calories <= calValueTo).ToList();
                        allRes.Add(calList);
                    }
                    if (item.Key == "fat")
                    {
                        int fatValueFrom = int.Parse(item.Value[0]);
                        int fatValueTo = int.Parse(item.Value[1]);
                        fatList = context.Recipes.Where(n => n.Fat >= fatValueFrom && n.Fat <= fatValueTo).ToList();
                        allRes.Add(fatList);
                    }
                    if (item.Key == "carbohydrate")
                    {
                        int carbValueFrom = int.Parse(item.Value[0]);
                        int carbValueTo = int.Parse(item.Value[1]);
                        carbList = context.Recipes.Where(n => n.Carbohydrate >= carbValueFrom && n.Carbohydrate <= carbValueTo).ToList();
                        allRes.Add(carbList);
                    }
                    if (item.Key == "protein")
                    {
                        int protValueFrom = int.Parse(item.Value[0]);
                        int protValueTo = int.Parse(item.Value[1]);
                        protList = context.Recipes.Where(n => n.Protein >= protValueFrom && n.Protein <= protValueTo).ToList();
                        allRes.Add(protList);
                    }
                }


                List<Recipe> finalList = new List<Recipe>();

                for (int i = 0; i < allRes.Count; i++)
                {
                    if (i == 0)
                    {
                        finalList = allRes[0];
                    }
                    else
                    {
                        finalList = finalList.Intersect(allRes[i]).ToList();
                    }
                }
                return finalList;
            }
        }


        //public List<Recipe> looseSearchByIngredients(List<string> searchlist)
        //{
        //    List<string> searchList = searchlist;
        //    using (NutrappDataContext context = new NutrappDataContext())
        //    {
        //        var looseLINQ = context.Recipes
        //                            .Where(r =>
        //                                context.Recipe_Ingredients
        //                                    .Where(ri =>
        //                                        context.Ingredients
        //                                            .Where(i => searchList.Contains(i.Name))
        //                                            .Select(i => i.ID)
        //                                            .Min() == ri.IID
        //                                    )
        //                                    .Select(ri => ri.RID)
        //                                .Contains(r.ID)
        //                            );
        //        return looseLINQ.ToList();
        //    }

        //}

        //public List<Recipe> strictSearchByIngredients(List<string> searchlist)
        //{

        //    List<string> searchList = searchlist;
        //    using (NutrappDataContext context = new NutrappDataContext())
        //    {

        //        var strictLINQ = from rec in context.Recipes
        //                         where
        //                             (from a in
        //                                  (
        //                                      (from b in context.Recipe_Ingredients
        //                                       where

        //                                           (from ing in context.Ingredients
        //                                            where
        //                                              searchList.Contains(ing.Name)
        //                                            select new
        //                                            {
        //                                                ing.ID
        //                                            }).Contains(new { ID = b.IID })
        //                                       group b by new
        //                                       {
        //                                           b.RID
        //                                       } into g
        //                                       select new
        //                                       {
        //                                           match = g.Count(p => p.RID != null),
        //                                           g.Key.RID,
        //                                           c =
        //                                             (from r in context.Recipe_Ingredients
        //                                              where
        //                                                r.RID == g.Key.RID
        //                                              select new
        //                                              {
        //                                                  r.IID
        //                                              }).Count(p => p.IID != null)
        //                                       }))
        //                              where
        //                                a.match == a.c
        //                              select new
        //                              {
        //                                  a.RID
        //                              }).Contains(new { RID = (int)rec.ID })
        //                         select rec;
        //        return strictLINQ.ToList();
        //    }

        //}
    }
}
